/** \file    BuildProfilesImpl.cpp
  * \brief   All nested loops of BuildProfiles and most processor time is located here.
  * \date    2001 - 2003
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/segm/src/BuildProfilesImpl.cpp,v 1.2 2005/04/04 04:10:16 cscom1r4 Exp $
  */

#include "BuildProfilesImpl.h"
#include "xtd/arr2d_wise_iterator.h"
#include <algorithm>
#include "std_min_max.h"

using img::fRGB;

//normalize profiles on pixels count in segments
static void normalizeProfiles( Segmentation &s )
{
	Segmentation::iterator si;
	for (si = s.segmentsBegin(); si != s.segmentsEnd(); ++si) {
		YATL::Arr2d::iterator di = si->profile.begin();
		YATL::Arr2i::iterator ui = si->usedPixels.begin();
		for (; di != si->profile.end(); ++di, ++ui)
			if (*ui)
				*di /= *ui;
	}
}

/////////////////////////////////////
// limitative functions definition //
/////////////////////////////////////

class limfNone {
public:
	double operator()( double d2 ) const 
		{ return d2; }
	void setDisp(double /*s2*/)
		{}
};

class limfThreshold {
public:
	double operator()( double d2 ) const
		{ return std::min( d2*_recip_s2, 1.0 ); }
	void setDisp(double s2)
		{ _recip_s2 = 1.0 / s2; }
private:
	double _recip_s2;
};

class limfSmooth {
public:
	double operator()( double d2 ) const 
		{ return d2/(d2+_s2); }
	void setDisp(double s2)
		{ _s2 = s2; }
private:
	double _s2;
};

///////////////////////////////
// suitable pixels selectors //
///////////////////////////////

class suitAll {
public:
	bool operator() (const fRGB &/*c*/) const
		{ return true; }
};

class suitAlpha {
public:
	bool operator() (const fRGB &c) const
		{ return c.a != 0.0f; }
};

///quick adds into profile difference of source pixels and a row (or col) of target pixels
//template< class LIMF >
//inline void profileCorrelationLine( const LIMF &limf, int count, double *p, const fRGB *s, const fRGB *t, int pstride = 1, int tstride = 1 ) {
//	for( int i = 0; i < count; ++i, t+=tstride, p+=pstride ) {
//		*p -= limf( s->SqDistance( *t ) );
//	}
//}

//////////
// main //
//////////

template <class LIMF, class SUITF>
static void buildProfileInternal1( 
	Distinction distinction,
	LIMF &limf,
	Segmentation &s, 
	const img::FImage &src, const img::FImage &tgt, 
	int size,
	bool activeOnly,
	SUITF &suitf
)
{
	bool correlation = distinction == DIST_CORRELATION;
	bool dispersion = !correlation;
	int w = src.Width();
	int h = src.Height();
	const int dblsize = 2*size+1;
	YATL::Arr2d arr(dblsize,dblsize); //used temporarily for storing one-point profiles

	//clear old profiles
	s.resizeProfiles(size, activeOnly);

	xtd::arr2d_wise_const_iterator<int> segmIt( s.segmentCoordMap(), 0, 0 ); //segments
	const fRGB *srcIt = src.begin(); //source image
	for( ; segmIt != s.segmentCoordMap().end(); ++segmIt, ++srcIt ) {
		int i = *segmIt;
		if( i == -1 ) continue;
		Segment *sg = s.pSegmentByIndex(i);
		if( activeOnly && !sg->active ) continue;
		limf.setDisp(sg->colorDispersion);

		//value to compare targets with
		const fRGB s = correlation ? *srcIt : img::convert(sg->meanColor);

		int tx,ty; //target coordinates
		int dx,dy; //displacements

		int z = segmIt.x() - size + sg->pdisplacement().x;
		if (z < 0 || z >=h)
			continue;

		ty = segmIt.y()-size+sg->pdisplacement().y;
		for( dy = 0; dy < dblsize; ++dy, ++ty ) {
			if( ty<0 ) continue;
			if( ty>=h ) break;
			const fRGB *t = tgt.BeginRow(ty) + z;
			double	*p = sg->profile.BeginRow(dy);
			int		*u = sg->usedPixels.BeginRow(dy);
			tx = segmIt.x()-size;
			dx = std::max( -tx, 0 );
			t += dx;
			p += dx;
			int end_dx = std::min( dblsize, w-tx );

			for (int i = 0; i < end_dx-dx; ++i, ++t, ++p, ++u)
				if (suitf(*t)) {
					++*u;
					*p -= limf(s.SqDistance(*t));
				}
		}
	}
	normalizeProfiles(s);
}

void buildProfileInternal( 
	Distinction distinction,
	LimitativeFunc limitativeFunc,
	Segmentation &s, 
	const img::FImage &src, const img::FImage &tgt, 
	int size,
	bool activeOnly
)
{
	switch( limitativeFunc ) {
	case LIMF_NONE:
		buildProfileInternal1(distinction, limfNone(), s, src, tgt, size, activeOnly, suitAll());
		break;
	case LIMF_SMOOTH:
		//top-thresholds on color norm must be ready in sg->colorDispersion, see upThrsInsteadDisp()
		buildProfileInternal1(distinction, limfSmooth(), s, src, tgt, size, activeOnly, suitAll());
		break;
	case LIMF_THRESHOLD:
		buildProfileInternal1(distinction, limfThreshold(), s, src, tgt, size, activeOnly, suitAll());
		break;
	default:
		assert(false);
	}
}

void Segmentation::buildCorrelationProfileWithTest(const float_image &tgt, 
	unsigned size, bool activeOnly)
{
	const float_image & src = *sourceImage();
	img::FImage xsrc, xtgt;
	img::share_data(xsrc, src);
	img::share_data(xtgt, tgt);
	buildProfileInternal1(DIST_CORRELATION, limfNone(), 
		*this, xsrc, xtgt, size, activeOnly, suitAlpha());
}

